package com.weactivity.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.beust.ah.A;
import com.weactivity.dto.*;
import com.weactivity.entity.Activity;
import com.weactivity.entity.Message;
import com.weactivity.entity.Resource;
import com.weactivity.entity.User;
import com.weactivity.exception.ActivityCreationException;
import com.weactivity.mapper.ActivityMapper;
import com.weactivity.mapper.UserMapper;
import com.weactivity.service.ActivityService;
import com.weactivity.service.MessageService;
import com.weactivity.service.ResourceService;
import com.weactivity.service.UserService;
import com.weactivity.utils.ConfictedActivityStruct;
import com.weactivity.utils.Constant;
import com.weactivity.utils.UserHolder;
import net.bytebuddy.asm.Advice;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

@Service
public class ActivityServiceImpl extends ServiceImpl<ActivityMapper, Activity>  implements ActivityService {

    @Autowired
    private ActivityMapper activityMapper;
    @Autowired
    private MessageService messageService;
    @Autowired
    private ResourceService resourceService;
    @Autowired
    private UserService userService;

    DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    /**
     * 插入单个活动
     * @param activity
     * @return
     */
    @Override
    public int insertAct(Activity activity) {
        //todo
        return 0;
    }

    /**
     * 首页展示活动的简单信息
     *
     * @return
     */
    @Override
    public List<ActivitySimpleDTO> showSimpleAll() {
        //取用户id
        //2024.5.24 出现空指针问题 ,UserHolder.getUser()返回空
        // 空指针
        UserDTO userDTO = UserHolder.getUser();


        Long userId = userDTO.getId();
        //通过用户id来展示用户所有的活动

        //通过用户id，在user_activity_role中寻找该用户id下的aid;
        List<ActivitySimpleDTO> activityList =  activityMapper.findById(userId);
        //将activityList转化层DTO对象

        return activityList;
    }

    /**
     * 返回用户所有活动的全部信息
     *
     * @return
     */
    @Override
    public List<Activity> selectAllActivity() {

       int userId = Math.toIntExact(UserHolder.getUser().getId());

      List<Activity> activityList =  activityMapper.selectAllActivity(userId);

        return activityList;
    }

    /**
     *
     * 查询用户id下所有未开始和正在进行的活动
     *
     * @return
     */

    @Override
    public List<Activity> selectValidActivity() {
        int userId = Math.toIntExact(UserHolder.getUser().getId());
        List<Activity> validActivityList =  activityMapper.selectValidActivity(userId);
        return validActivityList;
    }

    /**
     * 用户通过消息界面里面发送的活动通知来参加活动
     * @param activityId
     * @param validActivityList
     * @return
     */
    @Override
    public boolean ackJoinActivity(int activityId, List<Activity> validActivityList) {
        Activity joinActivity = activityMapper.findSingleByid(activityId);
        //获得要参加获得的开始时间和结束时间
        String beginTimeStr = joinActivity.getBeginTime();
        String endTimeStr = joinActivity.getEndTime();
        //转化为LocalDateTime格式
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        LocalDateTime beginTime = LocalDateTime.parse(beginTimeStr,dateTimeFormatter);
        LocalDateTime endTime = LocalDateTime.parse(endTimeStr,dateTimeFormatter);


        for (Activity a: validActivityList) {
            LocalDateTime aStartTime = LocalDateTime.parse(a.getBeginTime(),dateTimeFormatter);
            LocalDateTime aEndTime = LocalDateTime.parse(a.getEndTime(),dateTimeFormatter);
            //一共有4种时间冲突
            //1
            //如果 参加活动的开始时间 晚于(isAfter) 已有活动的开始时间  && 参加活动的结束时间 早于(isBefore) 已有活动的结束时间
            //时间冲突
            //2
            // 如果 参加活动的开始时间 早于(isBefore) 已有活动的开始时间  && 参加活动的结束时间 早于(isBefore) 已有活动的结束时间
            //时间冲突
            //3
            // 如果 参加活动的开始时间 晚于(isAfter) 已有活动的开始时间 && 参加活动的结束时间 早于 。。。。
            //不写了，换成没有时间冲突的判断更快
            //满足两个大条件中的任意一个说明不冲突,continue,如果两个都没满足说明时间冲突了，返回false
            if ((beginTime.isBefore(aStartTime) && endTime.isBefore(aStartTime)) || (beginTime.isAfter(aEndTime) && endTime.isAfter(aEndTime))){
                continue;
            }else {
                return false;
            }
        }
        return true;

    }

    /**
     * 得到用户id下所有的活动信息，要是用户已经参加了该活动怎么办？
     * 判断是否用户已经参加了该活动，参加则返回用户已参加信息，没有则继续
     * @param activityId
     * @param validActivityList
     * @return
     */
    @Override
    public boolean judgeJoined(int activityId, List<Activity> validActivityList) {
        for (Activity a: validActivityList) {
            if(activityId == a.getId()){
                return true;
            }
        }
        return false;

    }

    /**
     * 使用于 1.ActivityController.ackJoinActivity() -- 用户在接收消息界面时通过该接口参加活动
     *
     *  参加该活动，需要向user_activity_role表中插入值
     *  权限怎么设置
     *  目前默认是参与者，也就是权限2,角色2
     *  另外还有 活动id  用户id
     * @param activityId
     * @return
     */
    @Override
    public int insertUseUidAndAid(int activityId) {
        int userId = Math.toIntExact(UserHolder.getUser().getId());
        int count = activityMapper.insertUseUidAndAid(userId,activityId);
        return count;

    }

    /**
     *
     *
     * 根据activity创建一个活动
     * @param activity
     * @return
     */
    @Override
    public int newActivity(Activity activity) {
        int insert = activityMapper.insertAct(activity);
        return insert;
    }

    /**
     *当活动创建完成之后，通过用户的id数组发送默认的活动邀请通知
     *
     * @param participantIdList 接收信息的用户id数组
     * @return
     */
    @Override
    public boolean sendDefaultMessageToPartis(LinkedList<Integer> participantIdList) {
        UserDTO sendUser = UserHolder.getUser();
        SendMessageDTO sendMessageDTO = new SendMessageDTO();
        sendMessageDTO.setSendNickName(sendUser.getNickName());
        sendMessageDTO.setSendId(Math.toIntExact(sendUser.getId()));
        sendMessageDTO.setReceiveIdList(participantIdList);
        sendMessageDTO.setSubject(Constant.DEFAULT_ACTIVITY_SENDMESSAGE_SUBJECT);
        sendMessageDTO.setContent(Constant.DEFAULT_ACTIVITY_SENDMESSAGE_CONTENT);
        int count = messageService.sendAllMessage(sendMessageDTO);
        if(count == participantIdList.size()){
            return true;
        }

        return false;
    }

    /**
     * todo 待实现
     * 自定义消息通知，用户自定义通知消息
     * @param participantIdList
     * @return
     */

    @Override
    public boolean sendDIYMessageToPartis(LinkedList<Integer> participantIdList) {
        return false;
    }

    /**
     * 向activity_organizer用户表插入活动组织者
     * @param aid
     * @return
     */

    @Override
    public int insertActOrganizer(int aid) {
        int insertOrganizerCount = activityMapper.insertOrganizer(Math.toIntExact(UserHolder.getUser().getId()), aid);
        return insertOrganizerCount;
    }

    /**
     * 综合考虑ARP  Activity Resource Participant 进行活动的创建
     * todo 目前还未完成资源冲突时的活动调度和 考虑参加活动人员的信息
     * 开启事务
     *
     * @param activityCreateInfo
     * @return
     */
    @Transactional(rollbackFor = {Exception.class})
    @Override
    public Result createActByARP(ActivityCreateInfo activityCreateInfo) {

        Activity createActivity = activityCreateInfo.getActivity();
        LocalDateTime createActBeginTime = LocalDateTime.parse(createActivity.getBeginTime(), dateTimeFormatter);
        LocalDateTime createActEndTime = LocalDateTime.parse(createActivity.getEndTime(), dateTimeFormatter);

        Map<String, Object> map = new HashMap<>();

        //创建活动的用户信息
        UserDTO createUser = UserHolder.getUser();

        //todo grade,type转化函数没写完
        try {

            Activity activity = activityCreateInfo.getActivity();
            //(1)  先不管时间是否冲突，资源是否冲突，直接根据前端传递的信息新建一个活动
            //后续根据资源条件，人员条件进行判断该活动是否能够创建成功
            System.out.println("初始act:" + activity.toString());
            int insertActCount = this.newActivity(activity);
            if (0 == insertActCount) {

                //return Result.fail("创建活动失败");
                throw new ActivityCreationException("活动插入失败");
            }
            System.out.println(activity.toString());

            //通过配置实现
            //因为前端是没有传入活动id的,此时activity.id = 0,需要通过数据库自动生成id,生成id之后需要更新这个activity的id

            //(2)资源条件判断
            Map<String, String> resourceInfo = activityCreateInfo.getResourceInfo();
            //前端传输的几种情况
            // 1.资源名称，资源类型，资源重要度(就是资源等级)
            // 2.资源名称(用户指定具体的资源)
            // 3.资源类型，资源重要度(用户不指定具体的资源类型，由系统根据当前已有资源进行分配)
            //由资源名称，资源类型，资源重要度寻找用户需要的资源，同时判断该资源在活动时间段是否被占用
            Map<String, List<Resource>> usableAndConfictMap = resourceService.isConficted(resourceInfo, createActBeginTime, createActEndTime);
            List<Resource> confictResourceList = usableAndConfictMap.get("confict");
            List<Resource> usableResourceList = usableAndConfictMap.get("usable");
            System.out.println(confictResourceList.toString());
            System.out.println(usableResourceList.toString());
            //todo
            //如果资源冲突，需要根据活动信息,资源信息，人员信息进行判断
            //如果可用资源列表为0并且冲突资源列表不为0，那么就需要进行冲突解决了
            if (0 != confictResourceList.size() && usableResourceList.size() == 0) {

                //传入冲突的资源列表
                //解决活动资源冲突,返回可用或等价资源

                Map<String, List<Resource>> usableOrSimilarResourceMap = resourceService.settleResourceConfict(activity, resourceInfo, confictResourceList);
                //如果没有可用资源和类似资源
                //其它活动的资源被抢占则usableResources有元素
                List<Resource> usableResources = usableOrSimilarResourceMap.get("usable");
                List<Resource> similarResources = usableOrSimilarResourceMap.get("similar");
                //分三种情况
                //1.既抢占不了其他活动的资源，又没有类似资源
                if (0 == usableResources.size() && 0 == similarResources.size()) {
                    throw new ActivityCreationException("活动资源冲突且无类似资源,请选择其他时间段或则等待系统调度");
                    //return Result.fail("资源冲突，请另行选择资源或等待系统调度");
                }
                //resourceStatus = 0 表示无资源冲突，顺利的预约资源
                //resourceStatus = 1时表示 有资源冲突，无可用资源，返回等价资源
                //resourceStatus = 2时表示 有资源冲突，活动抢占其他活动的资源，有可用资源

                //2.抢占不了其他活动的资源，但有相似资源
                if (0 == usableResources.size() && 0 != similarResources.size()) {


                    //删除之前添加的活动
                    int count =  activityMapper.deleteById(activity.getId());
                    //return Result.ok(map);
                    if (0 == count){
                        throw new ActivityCreationException("活动无法创建,删除失败");
                    }
                    map.put("resourceStatus", 1);
                    map.put("resource", similarResources);
                    map.put("message","活动创建失败，返回相似资源");

                }else {
                    //3.抢占其他活动的资源
                    map.put("resourceStatus", 2);
                    map.put("resource", usableResources.get(0));
                    map.put("message","活动抢占其他活动资源，创建成功");
                    //return Result.ok(map);
                }

                if ("活动抢占其他活动资源，创建成功".equals(map.get("message"))){
                    return Result.ok(map);
                }
                throw new ActivityCreationException(map);
                //return Result.ok(map);
            }

            //如果资源不冲突
            //该活动占领该资源，该资源已被预约
            //需要在资源表中继续添加一行表示被预约的时间段
            Map<String,Object> fillMap = resourceService.fillResourceWithAct(activity, resourceInfo, usableResourceList);
            if (null == fillMap){
                throw new ActivityCreationException("资源消息添加时错误 346L");
            }

            int createResourceCount = (int) fillMap.get("count");
            //如果添加不成功
            if (0 == createResourceCount) {

                throw new ActivityCreationException("资源信息添加失败，请稍后再试");
                // return Result.fail("资源信息添加失败，请稍后再试");
            }
            Resource normalResource = (Resource) fillMap.get("resource");
            map.put("resourceStatus",0);
            map.put("resource",normalResource);


            //(3)  人员消息判断 实现中
            //3.1 相关数据结构的准备
            LinkedList<Integer> participantIdList = activityCreateInfo.getParticipantIdList();
            System.out.println(participantIdList.toString());
            //计算新建活动的冲突人数
            int activitySum = 0;
            //一个map，里面放有 创建活动当天该用户冲突的活动，如果不冲突则为空，如果冲突返回冲突的activity
            Map<Integer, List<Activity>> partConfictedActMap = new HashMap<>();

            for (Integer i : participantIdList) {
                //根据用户参加活动的idList
                List<Integer> activityIdList = activityMapper.selectPartActId(i);

                //如果activityIdList 为零
                if (0 == activityIdList.size()){
                    //说明用户没有参加活动，也就没有活动冲突
                    continue;
                }
                //不为零
                System.out.println(activityIdList.toString());

                //todo 可优化 根据新建活动的日期来获得当天的用户参加的活动
                //根据idList获得activityList
                System.out.println("activityIdList的长度为"+activityIdList.size());
                //需要对返回来的activityList进行深拷贝,而不是在返回的List上直接操作，因为每次如果输入相同，返回的引用也相同
                List<Activity> originalActivityList = activityMapper.selectActByList(activityIdList);
                if (originalActivityList == null){
                    System.out.println("activityList为空");
                    throw new ActivityCreationException("根据id查找活动出错");
                }
                //深拷贝
                List<Activity> activityList = new LinkedList<>();
                for (Activity originalActivity : originalActivityList) {
                    activityList.add(new Activity(originalActivity));
                }
                System.out.println("---------------activityList的size="+activityList.size());
                //对于每个用户的参加的活动，看是否与新创建的活动是否有时间冲突，得到有时间冲突的list
                Set<Integer> actSet = new HashSet<>();
                Iterator<Activity> iterator = activityList.iterator();
                while (iterator.hasNext()){
                    Activity confictAct = iterator.next();
                    LocalDateTime confictActBeginTime = LocalDateTime.parse(confictAct.getBeginTime(), dateTimeFormatter);
                    LocalDateTime confictActEndTime = LocalDateTime.parse(confictAct.getEndTime(), dateTimeFormatter);
                    if (confictActBeginTime.isAfter(createActEndTime) || confictActEndTime.isBefore(createActBeginTime)) {
                        iterator.remove();
                    } else {
                        // 如果时间段有重叠，什么都不做
                    }
                    if (0 == activityList.size()){
                        break;
                    }
                }

                if (0 == activityList.size()) {
                    //无冲突活动则放入null
                    //partConfictedActMap.put(i,null);
                } else {
                    //这个是活动冲突的人数
                    activitySum++;
                    //将新创建的活动加入到这个冲突队列中去,这个列表一般只有两个元素
                    if (actSet.add(activity.getId())){
                        activityList.add(activity);
                    }
                    System.out.println(activityList.toString());
                    partConfictedActMap.put(i, activityList);
                }
                System.out.println(partConfictedActMap.toString());
                System.out.println("活动冲突人数"+activitySum);
            }


            //如果没有人员冲突
            if (0 == activitySum) {
                //向管理者表添加创建活动的用户id
                int managerCount =  activityMapper.insertManagerById(createUser.getId(),activity.getId());
                //添加到user_activity_role表

                int managerUARCount =  activityMapper.insertManagerIntoUARById(createUser.getId(),activity.getId());
                if (!(1 == managerCount && 1 == managerUARCount)){
                    throw new ActivityCreationException("管理者信息插入失败");
                }

                //向参与者表添加参与用户
                for (Integer i: participantIdList) {
                    int partCount =  activityMapper.insertPartById(i,activity.getId());
                    if (0 == partCount){
                        throw new ActivityCreationException("参与者信息插入参与者表失败");
                    }
                }
                //添加到user_activity_role表
                for (Integer i: participantIdList) {
                    int partUARCount =  activityMapper.insertPartIntoUARById(i,activity.getId());
                    if (0 == partUARCount){
                        throw new ActivityCreationException("参与者信息插入失败");
                    }
                }

                //给参与者发送信息
                boolean isSendSuccess = this.sendDefaultMessageToPartis(activityCreateInfo.getParticipantIdList());

                if (!isSendSuccess) {
                    throw new ActivityCreationException("发送信息失败了");
                    //return Result.fail("发送信息失败了");
                }
                map.put("final","活动无资源冲突,无人员冲突,创建成功");
                return Result.ok(map);

            }

            Map<Integer, ConfictedActivityStruct> confictedActivityStructMap = new HashMap<>();
            //通过活动id去重
            Set<Integer> confictActIds = new HashSet<>();

            for (Map.Entry<Integer, List<Activity>> entry : partConfictedActMap.entrySet()) {

                //如果按照这个步骤来，一般来说与新创建活动的冲突的活动只有一个
                //以下代码也是基于与新建活动冲突的活动只有一个的情况来考虑的

                Activity confictAct = entry.getValue().get(0);
                if (confictActIds.add(confictAct.getId())) {
                    //添加到数据结构中去
                    ConfictedActivityStruct confictedActivityStruct = new ConfictedActivityStruct();
                    confictedActivityStruct.setId(confictAct.getId());
                    confictedActivityStruct.setGrade(confictAct.getGrade());
                    int total = activityMapper.countPart(confictAct.getId());
                    confictedActivityStruct.setTotal(total);
                    confictedActivityStruct.sumup();
                    confictedActivityStructMap.put(confictAct.getId(), confictedActivityStruct);
                } else {

                    ConfictedActivityStruct confictedActivityStruct = confictedActivityStructMap.get(confictAct.getId());
                    confictedActivityStruct.sumup();
                    System.out.println(confictedActivityStruct.toString());
                }
            }
            System.out.println(confictedActivityStructMap);

            //到这步一定有人员时间冲突
            ConfictedActivityStruct createActStruct = new ConfictedActivityStruct();
            createActStruct.setId(activity.getId());
            createActStruct.setGrade(activity.getGrade());
            //如果算上活动创建者
            createActStruct.setTotal(participantIdList.size());
            createActStruct.setConfictedPeopleSum(activitySum);
            confictedActivityStructMap.put(activity.getId(), createActStruct);
            System.out.println("createActStruct" + createActStruct);
            System.out.println("confictedActivityStructMap" + confictedActivityStructMap);
            //3.2 贪心算法
            //对于每个冲突的用户
            for (Map.Entry<Integer, List<Activity>> entry : partConfictedActMap.entrySet()) {
                //查看首选活动的当前参与人数是否低于或等于所需人数的80%。
                //如果大于等于0.8
                /*
                //之后再用循环
                List<Activity> entryValue = entry.getValue();
                for (Activity i:entryValue){
                }*/
                Integer userId = entry.getKey();
                Activity confictedActForUser = entry.getValue().get(0);
                Activity createActForUser = entry.getValue().get(1);
                //第二个活动就是新建的活动
                System.out.println("冲突活动的达标率:"+confictedActivityStructMap.get(confictedActForUser.getId()).getAttendance());;
                if (confictedActivityStructMap.get(confictedActForUser.getId()).isUpToStandard()) {
                    //说明第一个活动人数已经达标

                    if (confictedActivityStructMap.get(createActForUser.getId()).isUpToStandard()) {
                        //说明第二个活动，也就是这个新建活动的人数也已经达标
                        //这里不需要别的操作，就让活动冲突的人去参加原来需要参加的活动,毕竟先来后到
                    }
                    //第一个活动人数达标，第二个活动人数没有达标
                    //将第一个参加的活动的名单去除,保留第二个参加活动

                    int UARCount = activityMapper.deleteFromUARByAidAndUid(confictedActForUser.getId(), userId);
                    int partCount = activityMapper.deleteFromPartByAidAndUid(confictedActForUser.getId(), userId);
                    if (!(1 == UARCount && 1 == partCount)) {

                        throw new ActivityCreationException("冲突参与者的活动删除失败");

                    }

                    //新建活动的冲突数据结构中的count加1
                    createActStruct.countup();
                    //给用户发信息，告诉他之前参加的活动被系统调配了，需要参加另一个活动
                    //获得发送信息时间字符串
                    LocalDateTime now = LocalDateTime.now();
                    String nowStr = now.format(dateTimeFormatter);
                    int messageCount = messageService.systemSendSingleConfictMessage(userId, Constant.CONTENT_SYSTEM_MESSAGE_TO_CONFICT_USER, Constant.SUBJECT_SYSTEM_MESSAGE_TO_CONFICT_USRE,nowStr);
                    if (0 == messageCount) {
                        throw new ActivityCreationException("冲突信息发送失败");
                    }
                } else {
                    //冲突活动低于0.8
                    //不需要删除即可
                    //count++
                    confictedActivityStructMap.get(confictedActForUser.getId()).countup();
                }
            }
            if (createActStruct.isUpToStandard()){
                map.put("final","活动存在人员冲突，创建成功");
            }else {
                //删除新建活动

                //删除新建资源记录
                map.put("final","活动应到人数未达80%,活动创建失败");
                throw new ActivityCreationException(map);
            }

        } catch (ActivityCreationException e) {

            System.out.println(e.getMessage());
            throw e;
        }

        //boolean isCreate = createActivityDetect(activity);
        return Result.ok(map);

    }

    @Override
    public List<Integer> findManagerIdsByAid(int aid) {

        List<Integer> ids = activityMapper.findManagerIdsByAid(aid);

        return ids;
    }

    @Override
    public Activity searchActInfo(String keyword) {

      Activity activity =  activityMapper.searchActInfo(keyword);
      return activity;
    }

    @Override
    public int findRole(int aid) {
        UserDTO user = UserHolder.getUser();

      Integer role =  activityMapper.findRole(aid,user.getId());
      if (null == role){
          return Constant.USER_NO_IN_ACTIVITY;
      }
        return role;
    }

    @Override
    public int setManager(int aid, String keyword) {

        User user = userService.findByUserNameOrId(keyword);
        if (null == user){
            return 0;
        }
        int count = activityMapper.setManager(aid,user.getId());
        return count;

    }

    @Override
    public int deleteManager(int uid, int aid) {
       int count =  activityMapper.deleteManager(uid,aid);
       return count;
    }

    @Override
    public Activity findActivityById(Integer activityId) {

      return  activityMapper.findSingleByid(activityId);

    }

    /**
     * 查找参加活动的总人数
     * @param id
     * @return
     */
    @Override
    public int actPartCount(int id) {
       return activityMapper.countPart(id);
    }

    @Override
    public List<UARDTO> selectAllUser(int aid) {
        return activityMapper.selectAllUser(aid);
    }
}
